﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;

namespace MassIntellect.Drawing.Shapes
{
	public class Group : Shape, ICollection<Shape>
	{
		#region Поля

		protected HashSet<Shape> Shapes = new HashSet<Shape>();

		public Func<Shape, bool> FilterFunc;

		#endregion

		#region Свойства

		public IEnumerable<Shape> ShapesWithFilter
		{
			get
			{
				return (FilterFunc != null && IsFilterOn)
				       	? Shapes.Where(FilterFunc)
				       	: Shapes;
			}
		}

		private GraphicsPath gp;
		public override GraphicsPath GraphicsPath
		{
			get
			{
				gp = new GraphicsPath();
				foreach (var item in Shapes)
					gp.AddPath(item.GraphicsPath, false);
				return gp;
			}
		}

		public bool IsFilterOn { get; set; }

		private bool IsGrantInit
		{
			get
			{
				return Shapes.Count > 0;
			}
		}

		public override float X
		{
			get
			{
				if (IsGrantInit)
					return Shapes.Min(s => s.X);
				else
					return float.NaN;
			}
			set
			{
				if (IsGrantInit)
				{
					var x = X;
					if (value != x)
					{
						foreach (var item in Shapes)
							item.X += (value - x);
						OnPropertyChanged("X");
						OnPropertyChanged("Right");
					}
				}
			}
		}

		public override float Y
		{
			get
			{
				if (IsGrantInit)
					return Shapes.Min(s => s.Y);
				else
					return float.NaN;
			}
			set
			{
				if (IsGrantInit)
				{
					var y = Y;
					if (value != y)
					{
						foreach (var item in Shapes)
							item.Y += (value - y);
						OnPropertyChanged("Y");
						OnPropertyChanged("Bottom");
					}
				}
			}
		}

		public override float Width
		{
			get
			{
				if (IsGrantInit)
					return Shapes.Max(s => s.X + s.Width);
				else
					return float.NaN;
			}
			set
			{
				if (IsGrantInit)
				{
					var w = Width;
					if (value != w)
					{
						if (Math.Abs(value) < 1)
							value = 1;
						if (w != 0)
							foreach (var item in Shapes)
								item.Width = item.Width * (value / w);
						else
							foreach (var item in Shapes)
								item.Width = value;
						OnPropertyChanged("Width");
						OnPropertyChanged("Right");
					}
				}
			}
		}

		public override float Height
		{
			get
			{
				if (IsGrantInit)
					return Shapes.Max(s => s.Y + s.Height);
				else
					return float.NaN;
			}
			set
			{
				if (IsGrantInit)
				{
					var h = Height;
					if (value != h)
					{
						if (Math.Abs(value) < 1)
							value = 1;
						if (h != 0)
							foreach (var item in Shapes)
								item.Height = item.Height * (value / h);
						else
							foreach (var item in Shapes)
								item.Height = value;
						OnPropertyChanged("Height");
						OnPropertyChanged("Bottom");
					}
				}
			}
		}

		#endregion

		#region Методы

		public override void DrawToGraphics(Graphics gr)
		{
			if (IsVisible)
				foreach (var item in Shapes)
					item.DrawToGraphics(gr);
		}

		private bool contains(PointF pnt, IEnumerable<Shape> shp)
		{
			bool res = shp.Any();
			if (res)
				foreach (var item in shp)
					if (res = item.Contains(pnt))
						break;
			return res;
		}

		public override bool Contains(PointF pnt)
		{
			return contains(pnt, ShapesWithFilter);
		}

		public bool Contains(PointF pnt, Func<Shape, bool> func)
		{
			if (func != null)
				return contains(pnt, ShapesWithFilter.Where(func));
			else
				return contains(pnt, Shapes);
		}

		public Shape ShapeAt(PointF pnt)
		{
			return ShapesWithFilter.FirstOrDefault(s => s.Contains(pnt));
		}

		public override object Clone()
		{
			var sh = base.Cloning<Group>();
			foreach (var item in Shapes
				.Select(s => s.Clone())
				.OfType<Shape>())
				sh.Add(item);
			return sh;
		}

		public override string ToString()
		{
			return string.Format("Группа ({0}-элементов)", Count);
		}

		#endregion

		#region IEnumerable<Shape> Members

		public IEnumerator<Shape> GetEnumerator()
		{
			return Shapes.GetEnumerator();
		}

		#endregion

		#region IEnumerable Members

		IEnumerator IEnumerable.GetEnumerator()
		{
			return Shapes.GetEnumerator();
		}

		#endregion

		#region ICollection<Shape> Members

		public void Add(params Shape[] items)
		{
			foreach (var item in items)
				Add(item);
		}

		public void Add(Shape item)
		{
			if (item != this && !Shapes.Contains(item))
				Shapes.Add(item);
		}

		public void Clear()
		{
			Shapes.Clear();
		}

		public bool Contains(Shape item)
		{
			return Shapes.Contains(item);
		}

		public void CopyTo(Shape[] array, int arrayIndex)
		{
			Shapes.CopyTo(array, arrayIndex);
		}

		public int Count
		{
			get { return Shapes.Count; }
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public bool Remove(Shape item)
		{
			return Shapes.Remove(item);
		}

		#endregion
	}
}